/**
 * ReportGear(2011)
 */
package net.abbp.report.view.grid;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.Rectangle2D;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JToolTip;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.MouseInputAdapter;

import net.abbp.report.utils.ReportUtils;
import net.abbp.report.view.GridView;

import com.reportgear.core.swing.pane.base.JBaseDialog;
import com.reportgear.report.model.auxiliary.ColumnRow;
import com.reportgear.report.model.auxiliary.RowPropList;
import com.reportgear.report.model.auxiliary.RowType;
import com.reportgear.report.view.cursor.CursorUtils;
import com.reportgear.report.view.grid.pane.NumberInputPane;
import com.reportgear.report.view.grid.selection.GridSelection;
import com.reportgear.report.view.grid.selection.Selection;
import com.reportgear.report.view.grid.support.DefaultGridRowCRRenderer;
import com.reportgear.report.view.grid.support.GridCRRenderer;
import com.reportgear.report.view.grid.util.GraphHelper;

/**
 * Grid行头
 * 
 * @version 1.0 2011-3-14
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since Report 1.0
 * 
 */
public class GridRowHeader extends BaseGridComponent {
    private static final long serialVersionUID = 6302098521779923352L;

    /**
     * 鼠标事件处理
     */
    public class GridRowHeaderMouseHandler extends MouseInputAdapter {

        // 拖动行Index
        int dragRowIndex = 0;
        // 拖动类型
        private DragType dragType = DragType.DRAG_NONE;
        // 多行拖动结束Index
        private int endMultiSelectIndex = 0;
        // 支持拖动改变大小
        private boolean isDragPermited = false;
        // 多行多动选择
        private boolean isMultiRowsSelectDragPermited = false;
        // 行提示
        private JToolTip rowTip = null;
        // 多行拖动开始Index
        private int startMultiSelectIndex = 0;
        // 提示窗体
        private JWindow tipWindow = null;

        public GridRowHeaderMouseHandler() {
        }

        public String createToolTipString(double h, double th) {
            return "高度: " + h + "/" + th + "像素";
        }

        public String createToolTipString(int index) {
            return index + "R";
        }

        /**
         * 选择行
         * 
         * @param row
         *            行
         */
        private void doSelectRow(int row) {
            final GridSelection gridSel = gridView.getGridSelection();
            gridSel.setType(GridSelection.Type.CELL);
            this.startMultiSelectIndex = this.endMultiSelectIndex = row;
            gridSel.setEditBounds(0, row, 1, 1);
            gridSel.setOnlySelection(0, row, gridView.getColumnCount(), 1);

            gridView.fireGridSelectionChanged();
        }

        // 拖动多行
        private void dragMultiRows(final GridSelection gridSel, final int xPos, final int yPos) {
            if (!this.isMultiRowsSelectDragPermited) {
                return;
            }

            final ColumnRow cr = ReportUtils.getColumnRowByXY(gridView, xPos, yPos);

            this.endMultiSelectIndex = cr.getRow();
            final int rowSpan = Math.abs(this.endMultiSelectIndex - this.startMultiSelectIndex) + 1;
            final int startIndex = Math.min(this.startMultiSelectIndex, this.endMultiSelectIndex);

            gridSel.setEditBounds(0, startIndex, 1, 1);
            gridSel.setOnlySelection(0, startIndex, gridView.getColumnCount(), rowSpan);

            this.setToolTipText2(this.createToolTipString(rowSpan));

            gridView.fireGridSelectionChanged();
        }

        // 拖动单行
        private void dragSingleRow(final int yPos) {
            if (!this.isDragPermited) {
                return;
            }

            RowPropList rpl = reportModel.getRowPropList();

            int h = yPos - rpl.getRangeValue(gridView.getVertBeginValue(), this.dragRowIndex);
            rpl.setHeight(this.dragRowIndex, h);

            this.setToolTipText2(this.createToolTipString(rpl.getHeight(this.dragRowIndex), rpl.getRangeValue(0,
                    this.dragRowIndex + 1)));
        }

        private void hideToolTip() {
            if (this.tipWindow != null) {
                this.tipWindow.setVisible(false);
            }
        }

        // 是否可编辑
        private boolean isEditable() {
            return isEnabled();
        }

        private boolean isOnNormalSeparatorLine(double value, double refValue) {
            return Math.abs(value - refValue) < 2D;
        }

        @Override
        public void mouseDragged(MouseEvent evt) {
            if (!this.isEditable()) {
                return;
            }

            if (this.dragType == DragType.DRAG_NONE || SwingUtilities.isRightMouseButton(evt)) {
                return;
            }

            gridView.stopEditing();

            GridSelection gridSel = gridView.getGridSelection();
            gridSel.setType(GridSelection.Type.CELL);

            if (this.dragType == DragType.DRAG_SELECTED_ROWS) {// 多行选择
                this.dragMultiRows(gridSel, evt.getX(), evt.getY());
            } else {// 单行选择
                this.dragSingleRow(evt.getY());
            }

            gridView.repaint();
        }

        @Override
        public void mouseMoved(MouseEvent evt) {
            if (!this.isEditable()) {
                return;
            }

            setCursor(CursorUtils.CURSOR_V_SELECT);

            RowPropList rpl = reportModel.getRowPropList();

            double rowHeight = 0.0D;
            double tmpTotalHeight = 0.0D;

            final double height = getHeight();
            int pointer = gridView.getVertBeginValue();
            final double yPos = evt.getY();
            int maxRow = gridView.getMaxRowCount();
            while ((tmpTotalHeight < height) && (pointer < maxRow)) {
                rowHeight = rpl.getHeight(pointer);

                if (rowHeight <= 0.0D) {
                    rowHeight = 1.0D;
                }

                tmpTotalHeight += rowHeight;

                if (this.isOnNormalSeparatorLine(yPos, tmpTotalHeight)) {
                    setCursor(CursorUtils.CURSOR_V_MOVE);
                    return;
                }

                pointer++;
            }
        }

        @Override
        public void mousePressed(MouseEvent evt) {
            if (!this.isEditable()) {
                return;
            }

            gridView.stopEditing();
            int xPos = evt.getX();
            int yPos = evt.getY();
            final RowPropList rpl = reportModel.getRowPropList();
            double rowHeight = 0.0D;
            double tmpTotalHeight = 0.0D;
            double height = getSize().getHeight();

            int pointer = gridView.getVertBeginValue();
            int maxRow = gridView.getMaxRowCount();
            while ((tmpTotalHeight < height) && (pointer < maxRow)) {
                rowHeight = rpl.getHeight(pointer);

                if (rowHeight <= 0.0D) {
                    rowHeight = 1.0D;
                }

                tmpTotalHeight += rowHeight;

                if (this.isOnNormalSeparatorLine(yPos, tmpTotalHeight)) {
                    this.dragType = DragType.DRAG_HEIGHT;
                    this.isDragPermited = true;
                    this.dragRowIndex = pointer;
                    final MouseEvent event = evt;
                    SwingUtilities.invokeLater(new Runnable() {

                        public void run() {
                            showToolTip(event, GridRowHeaderMouseHandler.this.createToolTipString(rpl
                                    .getHeight(dragRowIndex), rpl.getRangeValue(0, dragRowIndex + 1)));
                        }
                    });

                    break;
                }

                if (yPos > (tmpTotalHeight - rowHeight) && yPos <= tmpTotalHeight) {
                    this.dragType = DragType.DRAG_SELECTED_ROWS;
                    this.isMultiRowsSelectDragPermited = true;
                    this.startMultiSelectIndex = pointer;
                    this.showToolTip(evt, this.createToolTipString(this.startMultiSelectIndex + 1));

                    break;
                }

                pointer++;
            }

            if (evt.getClickCount() > 1) {// 双击
                if (popupMenu == null) {
                    popupMenu = new GridRowPopupMenu();
                }
                this.hideToolTip();
            }

            if (SwingUtilities.isRightMouseButton(evt)) {// 右键
                if (popupMenu == null) {
                    popupMenu = new GridRowPopupMenu();
                }
                this.hideToolTip();
                popupMenu.show(GridRowHeader.this, xPos, yPos);
            } else if (this.dragType == DragType.DRAG_SELECTED_ROWS) {
                gridView.getGridSelection().setType(GridSelection.Type.CELL);

                ColumnRow columnRow = ReportUtils.getColumnRowByXY(gridView, xPos, yPos);
                this.doSelectRow(columnRow.getRow());
            }

            gridView.refresh();
        }

        @Override
        public void mouseReleased(MouseEvent evt) {
            if (!this.isEditable()) {
                return;
            }

            if (this.dragType == DragType.DRAG_NONE || SwingUtilities.isRightMouseButton(evt)) {
                return;
            }

            gridView.stopEditing();
            this.hideToolTip();

            GridSelection gridSel = gridView.getGridSelection();
            Selection selection = gridSel.getLastCellSelection();
            RowPropList rpl = reportModel.getRowPropList();

            // 修改所有选择的行的高度
            if (this.dragType == DragType.DRAG_HEIGHT) {
                final int row = selection.getRow();
                final int h = selection.getRowSpan();
                if ((this.dragRowIndex - row < h) && (this.dragRowIndex >= row)) {
                    for (int i = 0; i < h; i++) {
                        rpl.setHeight(row + i, rpl.getHeight(this.dragRowIndex));
                    }
                }
                gridView.fireReportDataChanged();
            }

            this.isMultiRowsSelectDragPermited = false;
            this.isDragPermited = false;
            this.dragType = DragType.DRAG_NONE;
            this.dragRowIndex = 0;

            gridView.refresh();
        }

        private void setToolTipText2(String s) {
            if (this.rowTip == null) {
                return;
            } else {
                this.rowTip.setTipText(s);
                this.rowTip.setSize(this.rowTip.getPreferredSize());
                this.tipWindow.pack();
                this.tipWindow.repaint();
                return;
            }
        }

        void showToolTip(MouseEvent event, String tips) {
            if (this.tipWindow == null) {
                this.tipWindow = new JWindow();
                this.rowTip = createToolTip();
                this.tipWindow.getContentPane().add(this.rowTip, BorderLayout.CENTER);
            }
            this.rowTip.setTipText(tips);
            Point p = new Point(0, event.getY());
            SwingUtilities.convertPointToScreen(p, GridRowHeader.this);
            Dimension dim = this.rowTip.getPreferredSize();
            int i = p.x + getSize().width + 2;
            int j = p.y - dim.height / 2;

            this.tipWindow.setLocation(i, j);
            this.tipWindow.pack();
            this.tipWindow.setVisible(true);
        }
    }

    /**
     * 弹出菜单
     */
    private class GridRowPopupMenu extends JPopupMenu {

        private static final long serialVersionUID = -6752711533691884927L;

        public GridRowPopupMenu() {
            this.add(new JMenuItem(new AddRowAction()));
            this.add(new JMenuItem(new DeleteRowAction()));
            this.addSeparator();
            this.add(new JMenuItem(new SetRowHeightAction()));
            this.addSeparator();
            this.add(new JMenuItem(new SetRepeatRowHeaderAction()));
            this.add(new JMenuItem(new SetRepeatRowFooterAction()));
            this.add(new JMenuItem(new CancelRepeatRowAction()));
        }

        private class AddRowAction extends AbstractAction {

            private static final long serialVersionUID = 2676223992260123762L;

            public AddRowAction() {
                super("插入行");
            }

            public void actionPerformed(ActionEvent e) {
                Selection selection = gridView.getLastCellSelection();

                gridView.getReportModel().addRow(selection.getRow());
                gridView.refresh();
            }
        }

        /**
         * 删除行
         */
        private class DeleteRowAction extends AbstractAction {

            private static final long serialVersionUID = 699713225940178948L;

            public DeleteRowAction() {
                super("删除行");
            }

            public void actionPerformed(ActionEvent e) {
                Selection selection = gridView.getLastCellSelection();

                int row = selection.getRow();
                int rowCount = selection.getRowSpan();

                for (int i = rowCount - 1; i >= 0; i--) {
                    reportModel.removeRow(row + i);
                }

                gridView.refresh();
            }
        }

        private class SetRowHeightAction extends AbstractAction {

            private static final long serialVersionUID = -5433859317286470060L;

            public SetRowHeightAction() {
                super("设置行高");
            }

            public void actionPerformed(ActionEvent e) {
                Selection selection = gridView.getLastCellSelection();

                int row = selection.getRow();
                int rowCount = selection.getRowSpan();

                NumberInputPane inputPane = new NumberInputPane();
                inputPane.setTitle("高度");
                inputPane.populate(reportModel.getRowHeight(row));

                if (inputPane.showDialog(null) == JBaseDialog.RESULT_OK) {
                    int value = (int) inputPane.update();
                    for (int i = 0; i < rowCount; i++) {
                        reportModel.setRowHeight(row + i, value);
                    }

                    gridView.refresh();
                }
            }
        }

        private class SetRepeatRowHeaderAction extends AbstractAction {

            private static final long serialVersionUID = -3638196366876593236L;

            public SetRepeatRowHeaderAction() {
                super("设置重复标题行");
            }

            public void actionPerformed(ActionEvent e) {
                Selection selection = gridView.getLastCellSelection();

                int row = selection.getRow();
                int rowCount = selection.getRowSpan();

                for (int i = 0; i < rowCount; i++) {
                    int index = row + i;

                    RowType type = reportModel.getRowType(index);

                    reportModel.setRowType(index, RowType.HEADER);
                }

                gridView.refresh();
            }
        }

        private class SetRepeatRowFooterAction extends AbstractAction {

            private static final long serialVersionUID = -981201755821471317L;

            public SetRepeatRowFooterAction() {
                super("设置重复结尾行");
            }

            public void actionPerformed(ActionEvent e) {
                Selection selection = gridView.getLastCellSelection();

                int row = selection.getRow();
                int rowCount = selection.getRowSpan();

                for (int i = 0; i < rowCount; i++) {
                    int index = row + i;

                    RowType type = reportModel.getRowType(index);

                    reportModel.setRowType(index, RowType.FOOTER);
                }

                gridView.refresh();
            }
        }

        private class CancelRepeatRowAction extends AbstractAction {

            private static final long serialVersionUID = 7810704078670911795L;

            public CancelRepeatRowAction() {
                super("取消重复行列");
            }

            public void actionPerformed(ActionEvent e) {
                Selection selection = gridView.getLastCellSelection();

                int row = selection.getRow();
                int rowCount = selection.getRowSpan();

                for (int i = 0; i < rowCount; i++) {
                    int index = row + i;

                    reportModel.setRowType(index, RowType.NORMAL);
                }

                gridView.refresh();
            }
        }
    }

    // 单元格Render
    private GridCRRenderer gridCRRender;
    // 弹出菜单
    public GridRowPopupMenu popupMenu;
    // 选中背景色
    private Color selectedBackground = new Color(253, 216, 153);
    // 选中前景色
    private Color selectedForeground = Color.BLACK;
    // 分隔线颜色
    private Color separatorLineColor = new Color(172, 168, 153);
    // 临时矩形对象
    private final Rectangle2D tmpRect = new Rectangle2D.Double(0, 0, 0, 0);

    /**
     * 构造函数
     */
    public GridRowHeader(GridView gridView) {
        super(gridView);

        final GridRowHeaderMouseHandler mouseHandler = new GridRowHeaderMouseHandler();
        this.addMouseListener(mouseHandler);
        this.addMouseMotionListener(mouseHandler);
        this.gridCRRender = new DefaultGridRowCRRenderer(this.reportModel);
        this.getInputMap().clear();
        this.getActionMap().clear();
        this.setFocusable(false);
        this.setOpaque(true);
    }

    /**
     * 获取标题绘制对象
     * 
     * @return 标题绘制对象
     */
    public GridCRRenderer getGridCRRender() {
        return this.gridCRRender;
    }

    @Override
    public Dimension getPreferredSize() {
        if (this.gridView.isRowHeaderVisible()) {
            return new Dimension(5 * this.getFontMetrics(this.getFont()).charWidth('M'),
                    super.getPreferredSize().height);
        } else {
            return new Dimension(0, 0);
        }
    }

    /**
     * 获取选中背景颜色
     * 
     * @return 背景颜色
     */
    public Color getSelectedBackground() {
        return this.selectedBackground;
    }

    /**
     * 获取选中前景色
     * 
     * @return 选中前景色
     */
    public Color getSelectedForeground() {
        return this.selectedForeground;
    }

    /**
     * 获取分隔线颜色
     * 
     * @return 分隔线颜色
     */
    public Color getSeparatorLineColor() {
        return this.separatorLineColor;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        final Graphics2D g2 = (Graphics2D) g;
        final Dimension dim = this.getSize();

        g2.setPaint(this.getBackground());
        GraphHelper.fill(g2, this.tmpRect);
        this.tmpRect.setRect(0.0D, 0.0D, dim.getWidth(), dim.getHeight());

        final FontRenderContext frContext = g2.getFontRenderContext();
        final LineMetrics lm = this.getFont().getLineMetrics("A", frContext);
        final float ascent = lm.getAscent();

        final RowPropList rpl = this.reportModel.getRowPropList();

        g2.setPaint(this.getSeparatorLineColor());
        GraphHelper.drawLine(g2, 0.0D, 0.0D, dim.getWidth(), 0.0D);
        final GridSelection gridSel = this.gridView.getGridSelection();

        int beginIndex = this.gridView.getVertBeginValue();
        int rowExtend = 0;

        double tmpHeight1 = 0.0D;
        double tmpHeight2 = 0.0D;
        double rowHeight = 0.0D;
        final double clientHeight = dim.getHeight();

        final List<Integer> selectedIndexList = ReportUtils.getGridSelectionRowIndex(gridSel);
        boolean tmpSelectStatus;

        int rowCount = this.gridView.isShowActualColumnRow() ? this.gridView.getRowCount() : Integer.MAX_VALUE;
        while ((tmpHeight2 < clientHeight) && (beginIndex <= rowCount)) {
            tmpSelectStatus = false;
            tmpHeight1 += rowHeight;
            rowHeight = rpl.getHeight(beginIndex);
            if (rowHeight == 0.0D) {
                tmpHeight2 = tmpHeight1 + 1.0D;
            } else {
                tmpHeight2 = tmpHeight1 + rowHeight;
            }

            if (selectedIndexList.contains(beginIndex)) {
                g2.setPaint(this.getSelectedBackground());
                GraphHelper.fill(g2, new Rectangle2D.Double(0.0D, tmpHeight1 + 1.0D, dim.width, rowHeight - 1.0D));
                tmpSelectStatus = true;
            } else {
                tmpSelectStatus = false;
            }

            if (beginIndex < rowCount) {
                g2.setColor(this.getSeparatorLineColor());
                GraphHelper.drawLine(g2, 0.0D, tmpHeight2, dim.getWidth(), tmpHeight2);
                final Object render = this.getGridCRRender().getDisplayInfo(beginIndex);
                if ((render instanceof String) || (render instanceof Number)) {
                    final String str = render.toString() + rpl.getTypeStringShort(beginIndex);

                    final double strWidth = this.getFont().getStringBounds(str, frContext).getWidth();
                    if (tmpSelectStatus) {
                        g2.setColor(this.getSelectedForeground());
                    } else if (this.isEnabled()) {
                        g2.setColor(this.getForeground());
                    } else {
                        g2.setPaint(UIManager.getColor("controlShadow"));
                    }

                    GraphHelper.drawString(g2, str, (dim.width - strWidth) / 2.0D, tmpHeight1 + 2.0D + ascent);
                }
            }
            beginIndex++;
            rowExtend++;
        }

        // 设置视图显示行数
        this.gridView.setVertValueExtend(rowExtend - 1);

        g2.setColor(this.getSeparatorLineColor());
        GraphHelper.drawLine(g2, 0.0D, 0.0D, 0.0D, clientHeight);
    }

    /**
     * 设置标题绘制对象
     * 
     * @param render
     *            绘制对象
     */
    public void setGridCRRender(GridCRRenderer render) {
        this.gridCRRender = render;
    }

    /**
     * 设置选中背景颜色
     * 
     * @param newColor
     *            新颜色
     */
    public void setSelectedBackground(Color newColor) {
        this.selectedBackground = newColor;

        this.repaint();
    }

    /**
     * 设置选中前景色
     * 
     * @param newColor
     *            新颜色
     */
    public void setSelectedForeground(Color newColor) {
        this.selectedForeground = newColor;

        this.repaint();
    }

    /**
     * 设置分隔线颜色
     * 
     * @param newColor
     *            新颜色
     */
    public void setSeparatorLineColor(Color newColor) {
        this.separatorLineColor = newColor;

        this.repaint();
    }
}
